using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
using XTCast.Mp3;
using System.Runtime.Serialization;

namespace XTCast.Radio
{
	public enum StatusType {Queued=1,Requested,Playing,History}

	[Serializable]
	public class Track:MP3File,IFrameReader
	{
		public StatusType Status {
			get {
				if (hasPlayed) return StatusType.History; 
				if (playing) return StatusType.Playing; 
				if (requested) return StatusType.Requested;
				return StatusType.Queued;
			}
		}

		private bool requested, playing, hasPlayed;
		private IFrameReader fr;
		private TimeSpan skip = TimeSpan.Zero;
		private TimeSpan position = TimeSpan.Zero;

		public bool Requested {
			set { requested = value; }
			get { return requested;	}
		}

		public bool Playing {
			set { playing = value; }
			get { return playing;	}
		}

		public bool HasPlayed {
			set { hasPlayed = value; }
			get { return hasPlayed;	}
		}

		public virtual TimeSpan Position
		{
			set
			{
				if (value<position) {
					Reset(); // position = 0 afterwards
				}
				if (value-position>TimeSpan.FromMilliseconds(250))
					skip = (value-position);
			}
			get
			{
				return position+skip;
			}
		}

		public Track():base() {
			AddToIndex(this);
		}

		public static Track GetTrack(int id) {
			return (Track)idIndex[id];
		}

		static Hashtable idIndex= new Hashtable();
		protected internal static void AddToIndex(Track f) {
			lock(idIndex) {
				idIndex[f.ID]=f;
			}
		}
		
		public Track(MP3File file):base(file)
		{
			AddToIndex(this);
		}

		public Track(SerializationInfo info, StreamingContext context):base(info, context) {
			AddToIndex(this);
			Position = (TimeSpan)info.GetValue("Position",typeof(TimeSpan));
		}

		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			info.AddValue("Position",Position);
			base.GetObjectData(info, context);
		}

		static Track _Empty ;
		public static Track Empty {
			get {
				if (_Empty==null) _Empty = new Track();
				return _Empty;
			}
		}

		public override string ToString() {
			if (this == Track.Empty) return "";
			return base.ToString ();
		}

		public void Reset() {
			fr.Reset();
			position = TimeSpan.Zero;
		}

		public void Open(int channels, int sampleRate, int bitrate, int maxBitrate) {
			position = TimeSpan.Zero;
			FileStream fs = new System.IO.FileStream(FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read,FileShare.ReadWrite);
			IFrameReader basefr = new MP3FrameReader(fs);
			if (basefr.Channels==channels && basefr.SampleRate==sampleRate && basefr.AvgBitrate<maxBitrate)
				fr=basefr;
			else
				fr=new MP3ReencodeFrameReader(basefr,bitrate,channels,sampleRate);
		}

		public void PreBuffer(int channels, int sampleRate) {
		}
	
		public void Close() {
			if (fr!=null) {
				position = TimeSpan.Zero;
				fr.Close();
				fr=null;
			}
		}
	
		public byte[] ReadFrames(FrameReadInfo info) {
			if (fr==null) throw new InvalidOperationException("Must call Open before using");
			if (skip>TimeSpan.Zero) {
				SkipFrames(new FrameReadInfo(int.MaxValue,(int)skip.TotalMilliseconds));
				skip = TimeSpan.Zero;
			}
			byte[] buf = fr.ReadFrames(info);
			position+=TimeSpan.FromMilliseconds(info.ActualMillis);
			return buf;
		}

		public void SkipFrames(FrameReadInfo info) {
			if (fr==null) throw new InvalidOperationException("Must call Open before using");
			fr.SkipFrames(info);
			position+=TimeSpan.FromMilliseconds(info.ActualMillis);
		}
	
		public int Channels {
			get {
				if (fr==null) throw new InvalidOperationException("Must call Open before using");
				return fr.Channels;
			}
		}
	
		public int SampleRate {
			get {
				if (fr==null) throw new InvalidOperationException("Must call Open before using");
				return fr.SampleRate;
			}
		}

		public int AvgBitrate {
			get {
				if (fr==null) throw new InvalidOperationException("Must call Open before using");
				return fr.AvgBitrate;
			}
		}
	}
}